package org.zebra.mybatis.plus.ext.redundancy;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.zebra.mybatis.plus.ext.redundancy.event.EntityUpdateEvent;

/**
 * @author zhanghongbin
 */
@Slf4j
@Intercepts(
        @Signature(
                type = Executor.class,
                method = "update",
                args = {MappedStatement.class, Object.class}))
public class RedundancyFieldInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        SqlCommandType commandType = ms.getSqlCommandType();
        Object obj = invocation.proceed();
        if (commandType == SqlCommandType.UPDATE && Integer.parseInt(obj.toString()) > 0) {
            Object parameter = invocation.getArgs()[1];
            BoundSql boundSql = ms.getBoundSql(parameter);
            String sql = boundSql.getSql();
            try {
                Statement statement = JsqlParserGlobal.parse(sql);
                if (statement instanceof Update) {
                    this.publishEvent(boundSql, statement);
                }
            } catch (Exception e) {
            }
        }
        return obj;
    }

    private void publishEvent(BoundSql boundSql, Statement statement) {
        MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) boundSql.getParameterObject();
        Object eventObj = null;
        if (paramMap.containsKey("et")) {
            eventObj = paramMap.get("et");
        } else if (paramMap.containsKey("param2")) {
            Update update = (Update) statement;
            String name = update.getTable().getFullyQualifiedName();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(name);
            if (tableInfo != null) {
                eventObj = BeanUtil.toBean(paramMap.get("params"), tableInfo.getEntityType());
            } else {
                log.error("无法获取表名为 " + name + " 的实体");
            }
        } else {
            log.error("BoundSql.getParameterObject 参数提取失败 " + JSONUtil.toJsonStr(paramMap));
        }
        if (eventObj != null) {
            // 发送事件
            SpringUtil.getApplicationContext().publishEvent(EntityUpdateEvent.create(eventObj));
        }
    }
}
